﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.IO;

namespace GameEngine.Libs
{
    /// <summary>
    /// Class representing an artificial neuron
    /// </summary>
    /// <remarks>
    /// <code>
    ///  
    ///  --------------> * W[0] \                              -----  
    ///  --------------> * W[1] - + -------> -threshold -------| f | ---------> O
    ///  --------------> * W[i] /                              -----
    ///     SYNAPSES      WEIGHT             THRESHOLD       ACTIVATION       OUTPUT
    ///
    /// </code>
    ///</remarks>
    //[Serializable]
    public class Neuron
    {
        #region PROTECTED FIELDS (State variables)

        /// <summary>
        /// Pseudo random number generator to initialize neuron weight
        /// </summary>
        protected static Random rand = new Random();
        /// <summary>
        /// Minimum value for randomisation of weights and threshold
        /// </summary>
        protected float R_MIN = -1;
        /// <summary>
        /// Maximum value for randomization of weights and threshold
        /// </summary>
        protected float R_MAX = 1;
        /// <summary>
        /// Weight of every synapse
        /// </summary>
        protected float[] w;
        /// <summary>
        /// Last weight of every synapse
        /// </summary>
        protected float[] last_w;
        /// <summary>
        /// Threshold of the neuron
        /// </summary>
        protected float threshold = 0f;
        /// <summary>
        /// Last threshold of the neuron
        /// </summary>
        protected float last_threshold = 0f;
        /// <summary>
        /// Activation function of the neuron
        /// </summary>
        protected IActivationFunction f = null;
        /// <summary>
        /// Value of the last neuron ouput
        /// </summary>
        protected float o = 0f;
        /// <summary>
        /// Last value of synapse sum minus threshold
        /// </summary>
        protected float ws = 0f;
        /// <summary>
        /// Usefull for backpropagation algorithm
        /// </summary>
        protected float a;

        #endregion

        #region PUBLIC ACCES TO STATE OF THE NEURON

        /// <summary>
        ///  Number of neuron inputs (synapses)
        /// </summary>
        public int N_Inputs
        {
            get { return w.Length; }
        }
        /// <summary>
        /// Indexer of the neuron to get or set weight of synapses
        /// </summary>
        public float this[int synapse]
        {
            get { return w[synapse]; }
            set { last_w[synapse] = w[synapse]; w[synapse] = value; }
        }
        /// <summary>
        /// To get or set the threshold value of the neuron
        /// </summary>
        public float Threshold
        {
            get { return threshold; }
            set { last_threshold = threshold; threshold = value; }
        }
        /// <summary>
        /// Get the last output of the neuron
        /// </summary>
        public float Output
        {
            get { return o; }
        }
        /// <summary>
        /// Get the last output prime of the neuron (f'(ws))
        /// </summary>
        public float OutputPrime
        {
            get { return f.OutputPrime(ws); }
        }
        /// <summary>
        /// Get the last sum of inputs
        /// </summary>
        public float WS
        {
            get { return ws; }
        }
        /// <summary>
        /// Get or set the neuron activation function
        /// </summary>
        public IActivationFunction F
        {
            get { return f; }
            set { f = value; }
        }
        /// <summary>
        /// Get or set a value of the neuron
        /// (usefull for backpropagation learning algorithm)
        /// </summary>
        public float A
        {
            get { return a; }
            set { a = value; }
        }
        /// <summary>
        /// Get the last threshold value of the neuron
        /// </summary>
        public float Last_Threshold
        {
            get { return last_threshold; }
        }
        /// <summary>
        /// Get the last weights of the neuron
        /// </summary>
        public float[] Last_W
        {
            get { return last_w; }
        }
        /// <summary>
        /// Get or set the minimum value for randomisation of weights and threshold
        /// </summary>
        public float Randomization_Min
        {
            get { return R_MIN; }
            set { R_MIN = value; }
        }
        /// <summary>
        /// Get or set the maximum value for randomization of weights and threshold
        /// </summary>
        public float Randomization_Max
        {
            get { return R_MAX; }
            set { R_MAX = value; }
        }

        #endregion

        #region NEURON CONSTRUCTOR

        /// <summary>
        /// Build a neurone with Ni inputs
        /// </summary>
        /// <param name="Ni">number of inputs</param>
        /// <param name="af">The activation function of the neuron</param>
        public Neuron(int Ni, IActivationFunction af)
        {
            w = new float[Ni];
            last_w = new float[Ni];
            f = af;
        }
        /// <summary>
        /// Build a neurone with Ni inputs whith a default 
        /// activation function (SIGMOID)
        /// </summary>
        /// <param name="Ni">number of inputs</param>
        public Neuron(int Ni)
        {
            w = new float[Ni];
            last_w = new float[Ni];
            f = new SigmoidActivationFunction();
        }

        #endregion

        #region PUBLIC METHODS (INITIALIZATION FUNCTIONS)

        /// <summary>
        /// Randomize Weight for each input between R_MIN and R_MAX
        /// </summary>
        public void randomizeWeight()
        {
            for (int i = 0; i < N_Inputs; i++)
            {
                w[i] = R_MIN + (((float)(rand.Next(1000))) / 1000f) * (R_MAX - R_MIN);
                last_w[i] = 0f;
            }
        }
        /// <summary>
        /// Randomize the threshold (between R_MIN and R_MAX)
        /// </summary>
        public void randomizeThreshold()
        {
            threshold = R_MIN + (((float)(rand.Next(1000))) / 1000f) * (R_MAX - R_MIN);
        }
        /// <summary>
        /// Randomize the threshold and the weights
        /// </summary>
        public void randomizeAll()
        {
            randomizeWeight();
            randomizeThreshold();
        }

        #endregion

        #region PUBLIC METHODS (COMPUTE THE OUTPUT VALUE)

        /// <summary>
        /// Compute the output of the neurone
        /// </summary>
        /// <param name="input">The input vector</param>
        /// <returns>The output value of the neuron ( =f(ws) )</returns>
        public float ComputeOutput(float[] input)
        {
            if (input.Length != N_Inputs)
                throw new Exception("NEURONE : Wrong input vector size, unable to compute output value");
            ws = 0;
            for (int i = 0; i < N_Inputs; i++)
                ws += w[i] * input[i];
            ws -= threshold;
            if (f != null)
                o = f.Output(ws);
            else
                o = ws;
            return o;
        }
        #endregion
    }

    /// <summary>
    /// A layer of neurone in a neuronal network
    /// </summary>
    /// <remarks>
    /// <code>
    ///             / N1 ----->        OUTPUTS
    /// INPUTS ===> - N2 ----->  (1 output for each 
    ///             \ Ni ----->  neuron of the layer)
    /// </code>       
    /// Each neuron of the layer has the same number of
    /// inputs, this is the number of inputs of the layer
    /// itself.
    /// </remarks>
    /// 
    //[Serializable]
    public class Layer
    {

        #region PROTECTED FIELDS (State of the layer)

        /// <summary>
        /// Number of neurons in the layer
        /// </summary>
        protected int nn;
        /// <summary>
        /// Number of inputs of the layer
        /// </summary>
        protected int ni;
        /// <summary>
        /// Neurons of the layer
        /// </summary>
        protected Neuron[] neurons;
        /// <summary>
        /// Last output of the layer
        /// </summary>
        protected float[] output;

        #endregion

        #region PUBLIC ACCES TO LAYER STATE

        /// <summary>
        /// Number of neurons in this layer
        /// </summary>
        public int N_Neurons
        {
            get { return nn; }
        }
        /// <summary>
        /// Number of inputs of this layer
        /// </summary>
        public int N_Inputs
        {
            get { return ni; }
        }
        /// <summary>
        /// Indexer of layer's neurons
        /// </summary>
        public Neuron this[int neurone]
        {
            get { return neurons[neurone]; }
        }
        /// <summary>
        /// Return last output vector of the layer
        /// </summary>
        public float[] Last_Output
        {
            get { return output; }
        }

        #endregion

        #region LAYER CONSTRUCTORS

        /// <summary>
        /// Build a new Layer with neurons neurones. Every neuron 
        /// has "inputs" inputs and the activation function f.
        /// </summary>
        /// <param name="inputs">Number of inputs</param>
        /// <param name="neurons">Number of neurons</param>
        /// <param name="f">Activation function of each neuron</param>
        public Layer(int neurons, int inputs, IActivationFunction f)
        {
            nn = neurons;
            ni = inputs;
            this.neurons = new Neuron[nn];
            output = new float[nn];
            for (int i = 0; i < neurons; i++)
                this.neurons[i] = new Neuron(inputs, f);
        }

        /// <summary>
        /// Build a new Layer with neurons neurones. Every neuron 
        /// has "inputs" inputs and the sigmoid activation function.
        /// </summary>
        /// <param name="inputs">Number of inputs</param>
        /// <param name="neurons">Number of neurons</param>
        public Layer(int neurons, int inputs)
        {
            nn = neurons;
            ni = inputs;
            this.neurons = new Neuron[nn];
            output = new float[nn];
            for (int i = 0; i < neurons; i++)
                this.neurons[i] = new Neuron(inputs);
        }

        /// <summary>
        /// Set the activation function f to all neurons of the layer
        /// </summary>
        /// <param name="f">An activation function</param>
        public void setActivationFunction(IActivationFunction f)
        {
            foreach (Neuron n in neurons)
                n.F = f;
        }

        #endregion

        #region INITIALIZATION FUNCTIONS

        /// <summary>
        /// Randomize all neurons weights
        /// </summary>
        public void randomizeWeight()
        {
            foreach (Neuron n in neurons)
                n.randomizeWeight();
        }
        /// <summary>
        /// Randomize all neurons thresholds
        /// </summary>
        public void randomizeThreshold()
        {
            foreach (Neuron n in neurons)
                n.randomizeThreshold();
        }
        /// <summary>
        /// Randomize all neurons threshold and weights
        /// </summary>
        public void randomizeAll()
        {
            randomizeWeight();
            randomizeThreshold();
        }
        /// <summary>
        /// Set the randomization interval for all neurons
        /// </summary>
        /// <param name="min">the minimum value</param>
        /// <param name="max">the maximum value</param>
        public void setRandomizationInterval(float min, float max)
        {
            foreach (Neuron n in neurons)
            {
                n.Randomization_Max = max;
                n.Randomization_Min = min;
            }
        }

        #endregion

        #region OUTPUT VALUE ACCES

        /// <summary>
        /// Compute output of the layer.
        /// The output vector contains the output of each 
        /// neuron of the layer.
        /// </summary>
        /// <param name="input">input of the layer (size must be N_inputs)</param>
        /// <returns>the output vector (size = N_neurons)</returns>
        public float[] Output(float[] input)
        {
            if (input.Length != ni)
                throw new Exception("LAYER : Wrong input vector size, unable to compute output value");
            for (int i = 0; i < nn; i++)
                output[i] = neurons[i].ComputeOutput(input);
            return output;
        }

        #endregion
    }

    /// <summary>
    /// Implementation of artificial neural network
    /// </summary>  
    /// <remarks>
    /// <code>
    /// 
    /// 
    ///                        o
    ///                        o  o  o  
    ///    INPUT VECTOR =====> o  o  o =====> OUTPUT VECTOR
    ///                        o  o  o  
    ///                        o
    ///                      NERON LAYERS
    /// 
    /// </code> 
    /// Each neuron of the layer N-1 is conected to 
    /// every neuron of the layer N.
    /// At the begining the neural network needs to
    /// learn using couples (INPUT, EXPECTED OUTPUT)
    /// and a learnig algorithm.
    /// </remarks>
    //[Serializable]
    public class NeuralNetwork
    {

        #region PROTECTED FIELDS (STATE OF THE NETWORK)

        /// <summary>
        /// Layers of neuron in the network
        /// </summary>
        protected Layer[] layers;
        /// <summary>
        /// Number of inputs of the network
        /// (number of inputs of the first layer)
        /// </summary>
        protected int ni;
        /// <summary>
        /// Learning algorithm used by the network
        /// </summary>
        protected LearningAlgorithm la;

        #endregion

        #region PUBLIC ACESS TO NETWORK STATE

        /// <summary>
        /// Get number of inputs of the network
        /// (network input vector size)
        /// </summary>
        public int N_Inputs
        {
            get { return ni; }
        }
        /// <summary>
        /// Get number of output of the network
        /// (network output vector size)
        /// </summary>
        public int N_Outputs
        {
            get { return layers[N_Layers - 1].N_Neurons; }
        }
        /// <summary>
        /// Get number of inputs of the network
        /// (network input vector size)
        /// </summary>
        public int N_Layers
        {
            get { return layers.Length; }
        }
        /// <summary>
        /// Get or set network learning algorithm
        /// </summary>
        public LearningAlgorithm LearningAlg
        {
            get { return la; }
            set { la = (value != null) ? value : la; }
        }
        /// <summary>
        /// Get the n th Layer of the network 
        /// </summary>
        public Layer this[int n]
        {
            get { return layers[n]; }
        }

        #endregion

        #region NEURAL NETWORK CONSTRUCTOR

        /// <summary>
        /// Create a new neural network
        /// with "inputs" inputs and size of "layers"
        /// layers of neurones.
        /// The layer i is made with layers_desc[i] neurones.
        /// The activation function of each neuron is set to n_act.
        /// The lerning algorithm is set to learn.
        /// </summary>
        /// <param name="inputs">Number of inputs of the network</param>
        /// <param name="layers_desc">Number of neurons for each layer of the network</param>
        /// <param name="n_act">Activation function for each neuron in the network</param>
        /// <param name="learn">Learning algorithm to be used by the neural network</param>
        public NeuralNetwork(int inputs, int[] layers_desc, IActivationFunction n_act, LearningAlgorithm learn)
        {
            if (layers_desc.Length < 1)
                throw new Exception("PERCEPTRON : cannot build perceptron, it must have at least 1 layer of neurone");
            if (inputs < 1)
                throw new Exception("PERCEPTRON : cannot build perceptron, it must have at least 1 input");
            la = learn;
            ni = inputs;
            layers = new Layer[layers_desc.Length];
            layers[0] = new Layer(layers_desc[0], ni);
            for (int i = 1; i < layers_desc.Length; i++)
                layers[i] = new Layer(layers_desc[i], layers_desc[i - 1], n_act);
        }
        /// <summary>
        /// Create a new neural network
        /// with "inputs" inputs and size of "layers"
        /// layers of neurones.
        /// The layer i is made with layers_desc[i] neurones.
        /// The activation function of each neuron is set to n_act.
        /// The lerning algorithm is set to default (Back Propagation).
        /// </summary>
        /// <param name="inputs">Number of inputs of the network</param>
        /// <param name="layers_desc">Number of neurons for each layer of the network</param>
        /// <param name="n_act">Activation function for each neuron in the network</param>
        public NeuralNetwork(int inputs, int[] layers_desc, IActivationFunction n_act)
        {
            if (layers_desc.Length < 1)
                throw new Exception("PERCEPTRON : cannot build perceptron, it must have at least 1 layer of neurone");
            if (inputs < 1)
                throw new Exception("PERCEPTRON : cannot build perceptron, it must have at least 1 input");
            la = new BackPropagationLearningAlgorithm(this);
            ni = inputs;
            layers = new Layer[layers_desc.Length];
            layers[0] = new Layer(layers_desc[0], ni);
            for (int i = 1; i < layers_desc.Length; i++)
                layers[i] = new Layer(layers_desc[i], layers_desc[i - 1], n_act);
        }
        /// <summary>
        /// Create a new neural network
        /// with "inputs" inputs and size of "layers"
        /// layers of neurones.
        /// The layer i is made with layers_desc[i] neurones.
        /// The activation function of each neuron is set to default (Sigmoid with beta = 1).
        /// The lerning algorithm is set to default (Back Propagation).
        /// </summary>
        /// <param name="inputs">Number of inputs of the network</param>
        /// <param name="layers_desc">Number of neurons for each layer of the network</param>
        public NeuralNetwork(int inputs, int[] layers_desc)
        {
            if (layers_desc.Length < 1)
                throw new Exception("PERCEPTRON : cannot build perceptron, it must have at least 1 layer of neurone");
            if (inputs < 1)
                throw new Exception("PERCEPTRON : cannot build perceptron, it must have at least 1 input");
            la = new BackPropagationLearningAlgorithm(this);
            ni = inputs;
            IActivationFunction n_act = new SigmoidActivationFunction();
            layers = new Layer[layers_desc.Length];
            layers[0] = new Layer(layers_desc[0], ni);
            for (int i = 1; i < layers_desc.Length; i++)
                layers[i] = new Layer(layers_desc[i], layers_desc[i - 1], n_act);
        }

        #endregion

        #region INITIALIZATION FUNCTIONS

        /// <summary>
        /// Randomize all neurones weights between -0.5 and 0.5
        /// </summary>
        public void randomizeWeight()
        {
            foreach (Layer l in layers)
                l.randomizeWeight();
        }
        /// <summary>
        /// Randomize all neurones threholds between 0 and 1
        /// </summary>
        public void randomizeThreshold()
        {
            foreach (Layer l in layers)
                l.randomizeThreshold();
        }
        /// <summary>
        /// Randomize all neurones threholds between 0 and 1
        /// and weights between -0.5 and 0.5
        /// </summary>
        public void randomizeAll()
        {
            foreach (Layer l in layers)
                l.randomizeAll();
        }
        /// <summary>
        /// Set an activation function to all neurons of the network
        /// </summary>
        /// <param name="f">An activation function</param>
        public void setActivationFunction(IActivationFunction f)
        {
            foreach (Layer l in layers)
                l.setActivationFunction(f);
        }
        /// <summary>
        /// Set the interval in which weights and threshold will be randomized
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        public void setRandomizationInterval(float min, float max)
        {
            foreach (Layer l in layers)
                l.setRandomizationInterval(min, max);
        }

        #endregion

        #region OUPUT METHODS

        /// <summary>
        /// Compute the value for the specified input
        /// </summary>
        /// <param name="input">the input vector</param>
        /// <returns>the output vector of the neuronal network</returns>
        public float[] Output(float[] input)
        {
            if (input.Length != ni)
                throw new Exception("PERCEPTRON : Wrong input vector size, unable to compute output value");
            float[] result;
            result = layers[0].Output(input);
            for (int i = 1; i < N_Layers; i++)
                result = layers[i].Output(result);
            return result;
        }

        public void Output(IInputVector input, IOutputVector output)
        {
            output.FromArray(Output(input.ToArray()));
        }

        #endregion

        #region PERSISTANCE IMPLEMENTATION
        /// <summary>
        /// Save the Neural Network in a binary formated file
        /// </summary>
        /// <param name="file">the target file path</param>
        public void save(string file)
        {
            /*TODO IFormatter binFmt = new BinaryFormatter();
            Stream s = File.Open(file, FileMode.Create);
            binFmt.Serialize(s, this);
            s.Close();*/
        }
        /// <summary>
        /// Load a neural network from a binary formated file
        /// </summary>
        /// <param name="file">the neural network file file</param>
        /// <returns></returns>
        public static NeuralNetwork load(string file)
        {
            /*TODO NeuralNetwork result;
            try
            {
                IFormatter binFmt = new BinaryFormatter();
                Stream s = File.Open(file, FileMode.Open);
                result = (NeuralNetwork)binFmt.Deserialize(s);
                s.Close();
            }
            catch (Exception e)
            {
                throw new Exception("NeuralNetwork : Unable to load file " + file + " : " + e);
            }
            return result;*/
            return null;
        }
        #endregion
    }

    public interface IInputVector
    {
        float[] ToArray();
    }

    public interface IOutputVector
    {
        void FromArray(float[] outputVector);
    }

    /// <summary>
    /// Interface of the activation function of a neuron.
    /// </summary>
    public interface IActivationFunction
    {
        /// <summary>
        /// Compute function value
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f(x)</returns>
        float Output(float x);
        /// <summary>
        /// Compute the diff of the function
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f'(x)</returns>
        float OutputPrime(float x);
    }

    #region SIGMOID ACTIVATION FUNCTION
    /// <summary>
    /// The sigmoid activation function
    /// </summary>
    /// <remarks>
    /// Here is the definition of the sigmoid activation function
    /// <code>
    ///                1
    /// f(x) = -----------------   beta > 0
    ///         1 + e^(-beta*x)
    /// 
    /// f'(x) = beta * f(x) * ( 1 - f(x) )   
    /// </code>     
    /// </remarks>
    //[Serializable]
    public class SigmoidActivationFunction : IActivationFunction
    {
        /// <summary>
        /// The beta parameter of the sigmoid
        /// </summary>
        protected float beta = 1.0f;

        /// <summary>
        /// Get or set the beta parameter of the function
        /// ( beta must be positive )
        /// </summary>
        public float Beta
        {
            get { return beta; }
            set { beta = (value > 0) ? value : 1.0f; }
        }
        /// <summary>
        /// Get the name of the activation function
        /// </summary>
        public string Name
        {
            get { return "Sigmoid"; }
        }
        /// <summary>
        /// <code>
        ///                 1
        /// f(x) = -----------------   beta > 0
        ///         1 + e^(-beta*x)
        /// </code>
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f(x)</returns>
        public virtual float Output(float x)
        {
            return (float)(1 / (1 + Math.Exp(-beta * x)));
        }
        /// <summary>
        /// <code>
        /// f'(x) = beta * f(x) * ( 1 - f(x) )
        /// </code>
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f'(x)</returns>
        public virtual float OutputPrime(float x)
        {
            float y = Output(x);
            return (beta * y * (1 - y));
        }
    }
    #endregion

    #region LINEAR ACTIVATION FUNCTION
    /// <summary>
    /// The linear activation function
    /// </summary>
    /// <remarks>
    /// <code>
    ///        |1            if x > 0.5/A
    /// f(x) = |A * x + 0.5  if 0.5/A > x > -0.5/A
    ///        |0            if -0.5/A > x
    /// 
    ///             A > 0      
    /// </code>
    /// </remarks>
    //[Serializable]
    public class LinearActivationFunction : IActivationFunction
    {
        /// <summary>
        /// The A parameter of the linear function
        /// </summary>
        protected float a = 1.0f;
        /// <summary>
        /// Usefull to compute function value
        /// </summary>
        protected float threshold = 0.5f;

        /// <summary>
        /// Get or set the A parameter of the function
        /// ( A must be positive )
        /// </summary>
        public float A
        {
            get { return a; }
            set
            {
                a = (value > 0) ? value : 1.0f;
                threshold = 0.5f / a;
            }
        }
        /// <summary>
        /// Get the name of the activation function
        /// </summary>
        public string Name
        {
            get { return "Linear"; }
        }
        /// <summary>
        ///  Get the activation function value
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f(x)</returns>
        public virtual float Output(float x)
        {
            if (x > threshold) return 1;
            else if (x < -threshold) return 0;
            else return a * x + 0.5f;
        }
        /// <summary>
        /// Get the diff function value
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f'(x)</returns>
        public virtual float OutputPrime(float x)
        {
            if (x > threshold) return 0;
            else if (x < -threshold) return 0;
            else return a;
        }
    }
    #endregion

    #region HEAVISIDE ACTIVATION FUNCTION
    /// <summary>
    /// The heaviside activation function
    /// </summary>
    /// <remarks>
    /// <code>
    /// f(x) = 0 if 0>x
    /// f(x) = 1 if x>0
    /// </code>   
    /// </remarks>
    //[Serializable]
    public class HeavisideActivationFunction : IActivationFunction
    {
        /// <summary>
        /// Get the name of the activation function
        /// </summary>
        public string Name
        {
            get { return "Heaviside"; }
        }
        /// <summary>
        ///  Get the heaviside function value
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f(x)</returns>
        public virtual float Output(float x)
        {
            if (x > 0) return 1;
            else return 0;
        }
        /// <summary>
        /// Get the derivative function value
        /// Simulate an impulse at origin...
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f'(x)</returns>
        public virtual float OutputPrime(float x)
        {
            if (Math.Abs(x) < 0.0001) return float.MaxValue;
            else return 0;
        }
    }
    #endregion

    #region GAUSSIAN ACTIVATION FUNCTION
    /// <summary>
    /// The gaussian activation function
    /// </summary>
    /// <remarks>
    /// <code>
    /// 
    ///                  1                -(x-mu)^2 / (2 * sigma^2)
    /// f(x)  =  -------------------- *  e
    ///          sqrt(2 * pi * sigma)
    /// 
    /// f'(x)  =  y(x) * -2*K*(x - mu) 
    /// </code>
    /// To implement a more efficient computation :
    /// <code>
    /// C = 1/sqrt(2 * pi * sigma)
    /// K = 1/(2 * sigma^2)
    /// </code>
    /// </remarks>
    //[Serializable]
    public class GaussianActivationFunction : IActivationFunction
    {
        /// <summary>
        /// The sigma parameter of the gaussian
        /// </summary>
        protected float sigma = 0.159155f;
        /// <summary>
        /// The mu parameter of the gaussian
        /// </summary>
        protected float mu = 0f;
        /// <summary>
        /// C parameter (usfull for computing function value)
        /// </summary>
        protected float C;
        /// <summary>
        /// C parameter (usfull for computing function value)
        /// </summary>
        protected float K;

        /// <summary>
        /// Get or set the sigma parameter of the function
        /// (sigma must be positive)
        /// </summary>
        public float Sigma
        {
            get { return sigma; }
            set
            {
                sigma = (value > 0) ? value : sigma;
                computeCK();
            }
        }
        /// <summary>
        /// Get or set the mu parameter of the function
        /// </summary>
        public float Mu
        {
            get { return mu; }
            set { mu = value; }
        }
        /// <summary>
        /// Compute C and K parameters from sigma
        /// </summary>
        protected void computeCK()
        {
            C = 1f / (float)Math.Sqrt(2 * Math.PI * sigma);
            K = 1 / (2 * sigma * sigma);
        }
        /// <summary>
        /// GaussianActivationFunction constructor
        /// </summary>
        public GaussianActivationFunction()
        {
            computeCK();
        }
        /// <summary>
        /// Get the name of the activation function
        /// </summary>
        public string Name
        {
            get { return "Gaussian"; }
        }
        /// <summary>
        /// Compute the value of the gaussian function
        /// <param name="x">x</param>
        /// <returns>f(x)</returns>
        /// </summary>
        public virtual float Output(float x)
        {
            return C * (float)Math.Exp(-(x - mu) * (x - mu) * K);
        }
        /// <summary>
        /// compute the derivative value of function
        /// </summary>
        /// <param name="x">x</param>
        /// <returns>f'(x)</returns>
        public virtual float OutputPrime(float x)
        {
            float y = Output(x);
            return -2 * y * K * (x - mu);
        }
    }
    #endregion

    /// <summary>
    /// The abstract class describing a learning
    /// algorithm for a neural network
    /// </summary>
    //[Serializable]
    public abstract class LearningAlgorithm
    {

        #region PROTECTED FIELDS

        /// <summary>
        /// The neural network
        /// </summary>
        protected NeuralNetwork nn;
        /// <summary>
        /// Under this threshold value, learning will be
        /// considered as complete
        /// </summary>
        protected float ERROR_THRESHOLD = 0.001f;
        /// <summary>
        /// Max number of iteration to learn data
        /// </summary>
        protected int MAX_ITER = 1000;
        /// <summary>
        /// Input matrix of data to learn
        /// </summary>
        protected float[][] ins;
        /// <summary>
        /// output matrix of data to learn
        /// </summary>
        protected float[][] outs;
        /// <summary>
        /// Number of learning iterations done
        /// </summary>
        protected int iter = 0;
        /// <summary>
        /// Last sum of square errors computed
        /// </summary>
        protected float error = -1;

        #endregion

        #region PUBLIC ACCES TO LEARNING ALGORITHM STATE
        /// <summary>
        /// Get the neural network of the learning algorithm
        /// </summary>
        public NeuralNetwork N_Network
        {
            get { return nn; }
        }
        /// <summary>
        /// Get the last square error
        /// </summary>
        public float Error
        {
            get { return error; }
        }
        /// <summary>
        /// Get or set the maximum sum of square errors value ( >0)
        /// </summary>
        public float ErrorTreshold
        {
            get { return ERROR_THRESHOLD; }
            set { ERROR_THRESHOLD = (value > 0) ? value : ERROR_THRESHOLD; }
        }
        /// <summary>
        /// Get the current number of learning iterations done
        /// </summary>
        public int Iteration
        {
            get { return iter; }
        }
        /// <summary>
        /// Get or set the maximum number of learning iterations.
        /// </summary>
        public int MaxIteration
        {
            get { return MAX_ITER; }
            set { MAX_ITER = (value > 0) ? value : MAX_ITER; }
        }

        #endregion

        #region CONSTRICTOR AND METHODS

        /// <summary>
        /// Learning algorithm constructor
        /// </summary>
        /// <param name="n">The neural network to train</param>
        public LearningAlgorithm(NeuralNetwork n)
        {
            nn = n;
        }
        /// <summary>
        /// To train the neuronal network on data.
        /// inputs[n] represents an input vector of 
        /// the neural network and expected_outputs[n]
        /// the expected ouput for this vector. 
        /// </summary>
        /// <param name="inputs">the input matrix</param>
        /// <param name="expected_outputs">the expected output matrix</param>
        public virtual void Learn(float[][] inputs, float[][] expected_outputs)
        {
            if (inputs.Length < 1)
                throw new Exception("LearningAlgorithme : no input data : cannot learn from nothing");
            if (expected_outputs.Length < 1)
                throw new Exception("LearningAlgorithme : no output data : cannot learn from nothing");
            if (inputs.Length != expected_outputs.Length)
                throw new Exception("LearningAlgorithme : inputs and outputs size does not match : learning aborded ");
            ins = inputs;
            outs = expected_outputs;
        }
        #endregion

    }

    #region BackPropagationLearningAlgorithm

    /// <summary>
    /// Implementation of stockastic gradient backpropagation
    /// learning algorithm
    /// </summary>
    /// <remarks>
    /// <code>
    /// 
    ///                      PROPAGATION WAY IN NN
    ///                    ------------------------->
    /// 
    ///        o ----- Sj = f(WSj) ----> o ----- Si = f(WSi) ----> o
    ///      Neuron j                Neuron i                   Neuron k
    ///    (layer L-1)               (layer L)                 (layer L+1)
    /// 
    /// For the neuron i :
    /// -------------------
    /// W[i,j](n+1) = W[i,j](n) + alpha * Ai * Sj + gamma * ( W[i,j](n) - W[i,j](n-1) )
    /// T[i](n+1) = T[i](n) - alpha * Ai + gamma * ( T[i](n) - T[i](n-1) )
    /// 
    ///		with :
    ///				Ai = f'(WSi) * (expected_output_i - si) for output layer
    ///				Ai = f'(WSi) * SUM( Ak * W[k,i] )       for others
    /// 
    /// </code>
    /// NOTE : This is stockastic version of the algorithm because the error
    /// is back-propaged after every learning case. There is another version
    /// of this algorithm which works on global error.
    /// </remarks>
    //[Serializable]
    public class BackPropagationLearningAlgorithm : LearningAlgorithm
    {

        #region PRETECTED FIELDS
        /// <summary>
        /// the alpha parameter of the algorithm
        /// </summary>
        protected float alpha = 0.5f;
        /// <summary>
        /// the gamma parameter of the algorithm
        /// </summary>
        protected float gamma = 0.2f;
        /// <summary>
        /// The error vector
        /// </summary>
        protected float[] e;

        #endregion

        #region PUBLIC ACCES TO PARAMETERS OF ALGORITHM

        /// <summary>
        /// get or set the alpha parameter of the algorithm
        /// between 0 and 1, must be >0
        /// </summary>
        public float Alpha
        {
            get { return alpha; }
            set { alpha = (value > 0) ? value : alpha; }
        }
        /// <summary>
        /// get or set the gamma parameter of the algorithm
        /// (Rumelhart coef)
        /// between 0 and 1.
        /// </summary>
        public float Gamma
        {
            get { return gamma; }
            set { gamma = (value > 0) ? value : gamma; }
        }

        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Build a new BackPropagation learning algorithm instance
        /// with alpha = 0,5 and gamma = 0,3
        /// </summary>
        /// <param name="nn">The neural network to train</param>
        public BackPropagationLearningAlgorithm(NeuralNetwork nn)
            : base(nn)
        {
        }

        #endregion

        #region LEARNING METHODS

        /// <summary>
        /// To train the neuronal network on data.
        /// inputs[n] represents an input vector of 
        /// the neural network and expected_outputs[n]
        /// the expected ouput for this vector. 
        /// </summary>
        /// <param name="inputs">the input matrix</param>
        /// <param name="expected_outputs">the expected output matrix</param>
        public override void Learn(float[][] inputs, float[][] expected_outputs)
        {
            base.Learn(inputs, expected_outputs);
            float[] nout;
            float err;
            iter = 0;
            do
            {
                error = 0f;
                e = new float[nn.N_Outputs];
                for (int i = 0; i < ins.Length; i++)
                {
                    err = 0f;
                    nout = nn.Output(inputs[i]);
                    for (int j = 0; j < nout.Length; j++)
                    {
                        e[j] = outs[i][j] - nout[j];
                        err += e[j] * e[j];
                    }
                    err /= 2f;
                    error += err;
                    ComputeA(i);
                    setWeight(i);
                }
                iter++;
            }
            while (iter < MAX_ITER && this.error > ERROR_THRESHOLD);
        }
        /// <summary>
        /// Compute the "A" parameter for each neuron
        /// of the network
        /// </summary>
        /// <param name="i">the index of the curent training data</param>
        protected void ComputeA(int i)
        {
            float sk;
            int l = nn.N_Layers - 1;
            // For the last layer
            for (int j = 0; j < nn[l].N_Neurons; j++)
                nn[l][j].A = nn[l][j].OutputPrime * e[j];
            // For other layer
            for (l--; l >= 0; l--)
            {
                for (int j = 0; j < nn[l].N_Neurons; j++)
                {
                    sk = 0f;
                    for (int k = 0; k < nn[l + 1].N_Neurons; k++)
                        sk += nn[l + 1][k].A * nn[l + 1][k][j];
                    nn[l][j].A = nn[l][j].OutputPrime * sk;
                }
            }
        }
        /// <summary>
        /// Set new neron's weights
        /// </summary>
        /// <param name="i">the index of the curent training data</param>
        protected void setWeight(int i)
        {
            float[] lin;
            for (int j = 0; j < nn.N_Layers; j++)
            {
                if (j == 0) lin = ins[i];
                else lin = nn[j - 1].Last_Output;
                for (int n = 0; n < nn[j].N_Neurons; n++)
                {
                    for (int k = 0; k < nn[j][n].N_Inputs; k++)
                        nn[j][n][k] += alpha * lin[k] * nn[j][n].A + gamma * (nn[j][n][k] - nn[j][n].Last_W[k]);
                    nn[j][n].Threshold -= alpha * nn[j][n].A + gamma * (nn[j][n].Threshold - nn[j][n].Last_Threshold);
                }
            }
        }

        #endregion



    }
    #endregion

    #region GeneticLearningAlgorithm

    /// <summary>
    /// A genetic learning algorithm
    /// </summary>
    /// <remarks>
    /// This is an aplication of genetic algorithm to train neural networks.
    /// The population is made of GeneticNeuralNetwork instance which is a
    /// compact representation of neural network. A genetic neural network
    /// represent a set of weights and threshold for a particular neural network.
    /// 
    /// Here is the main loop of the algorithm :
    /// <code>
    ///	  Create initial random population of POPULATION_SIZE neural networks
    ///	   -> Evaluate fitness function (square error on learning values)
    ///	  |   Select best neural networks
    ///	  |   Cross selected networks to make new generation
    ///    -- apply mutation operator on new generation
    ///	  Until error > error_threshold  	   
    /// </code>
    ///</remarks>
    //[Serializable]
    public class GeneticLearningAlgorithm : LearningAlgorithm
    {

        #region PROTECTED FIELDS AND PROPERTIES
        /// <summary>
        /// The random number generator
        /// </summary>
        protected static Random rand = new Random();
        /// <summary>
        /// The population size
        /// </summary>
        protected int POPULATION_SIZE = 50;
        /// <summary>
        /// The mutation ratio during crossover
        /// </summary>
        protected int MUTATION_RATIO = 4;
        /// <summary>
        /// Maximum mutation amplitude
        /// </summary>
        protected float MAX_MUTATION_AMP = 1f;
        /// <summary>
        /// The ratio of population selected for crossover
        /// </summary>
        protected int SELECTION_RATIO = 10;
        /// <summary>
        /// The population of GeneticNeuralNetwork
        /// </summary>
        protected List<GeneticNeuralNetwork> population;

        #endregion

        #region PUBLIC ACCES TO ALGORITHM STATE

        /// <summary>
        /// Get or set the population size
        /// </summary>
        public int PopulationSize
        {
            get { return POPULATION_SIZE; }
            set
            {

                List<GeneticNeuralNetwork> newPop = new List<GeneticNeuralNetwork>();
                int index = 0;
                population.Sort();
                while (index < value && index < population.Count)
                {
                    newPop.Add(population[index]);
                    index++;
                }
                while (index < value)
                {
                    newPop.Add(Muted_NeuralNetwork);
                    index++;
                }
                POPULATION_SIZE = value;
            }
        }
        /// <summary>
        /// Get or set the mutation ratio (between 0 and 100)
        /// </summary>
        public int MutationRatio
        {
            get { return MUTATION_RATIO; }
            set { MUTATION_RATIO = value; }
        }
        /// <summary>
        /// Get or set the maximum mutation amplitude
        /// </summary>
        public float MaxMutationAmplitude
        {
            get { return MAX_MUTATION_AMP; }
            set { MAX_MUTATION_AMP = value; }
        }
        /// <summary>
        /// get or set the selection ratio
        /// </summary>
        public int SelectionRatio
        {
            get { return SELECTION_RATIO; }
            set { SELECTION_RATIO = value; }
        }
        #endregion

        #region GENETIC ALGORITHM IMPLEMENTATION
        /// <summary>
        /// Get the random amplitude of a mutation
        /// </summary>
        protected float MutationValue
        {
            get { return (((float)rand.Next(20000) - 10000f) / 10000) * MAX_MUTATION_AMP; }
        }
        /// <summary>
        /// Get a bool with MUTATION_RATIO/100 probability to be true
        /// </summary>
        protected bool Mute
        {
            get { return (rand.Next(100) < MUTATION_RATIO); }
        }
        /// <summary>
        /// Get a random selected neural network in the population
        /// </summary>
        protected int RandSelectionIndex
        {
            get { return rand.Next(POPULATION_SIZE * SELECTION_RATIO / 100); }
        }
        /// <summary>
        /// get a muted GeneticNeuralNetwork from the neural network
        /// </summary>
        protected GeneticNeuralNetwork Muted_NeuralNetwork
        {
            get
            {
                GeneticNeuralNetwork result = new GeneticNeuralNetwork(nn);
                result.Init();
                for (int i = 0; i < result.N_Genes; i++)
                    if (Mute)
                        result[i] += MutationValue;
                return result;
            }
        }
        /// <summary>
        /// Define the crossover operator for 2 GeneticNeuralNetwork
        /// </summary>
        /// <param name="i1">index of mother in population</param>
        /// <param name="i2">index of father in population</param>
        /// <returns>the child</returns>
        protected GeneticNeuralNetwork CrossOver(int i1, int i2)
        {
            GeneticNeuralNetwork result = new GeneticNeuralNetwork(nn);
            GeneticNeuralNetwork ind1 = (GeneticNeuralNetwork)population[i1];
            GeneticNeuralNetwork ind2 = (GeneticNeuralNetwork)population[i2];
            int t = rand.Next(ind1.N_Genes);
            int index = 0;
            while (index < t)
            {
                result[index] = ind1[index];
                if (Mute)
                    result[index] += MutationValue;
                index++;
            }
            while (index < ind1.N_Genes)
            {
                result[index] = ind2[index];
                if (Mute)
                    result[index] += MutationValue;
                index++;
            }
            return result;
        }
        /// <summary>
        /// Compute the new generation
        /// </summary>
        protected void makeNewGeneration()
        {
            List<GeneticNeuralNetwork> result = new List<GeneticNeuralNetwork>();
            population.Sort();
            result.Add(population[0]);
            int index = 1;
            while (index < POPULATION_SIZE)
            {
                result.Add(CrossOver(RandSelectionIndex, RandSelectionIndex));
                index++;
            }
            population = result;
        }
        /// <summary>
        /// Computes square error for each GeneticNeuralNetwork in population
        /// </summary>
        protected void ComputeErrors()
        {
            float[] nout;
            float err;
            float[] e = new float[nn.N_Outputs];

            foreach (GeneticNeuralNetwork ind in population)
            {
                ind.setWeights();
                ind.Error = 0f;
                for (int i = 0; i < ins.Length; i++)
                {
                    err = 0f;
                    nout = nn.Output(ins[i]);
                    for (int j = 0; j < nout.Length; j++)
                    {
                        e[j] = outs[i][j] - nout[j];
                        err += e[j] * e[j];
                    }
                    err /= 2f;
                    ind.Error += err;
                }
            }
        }

        #endregion

        #region PUBLIC METHODS AND CONSTRUCTOR

        /// <summary>
        /// GeneticLearningAlgorithm constructor
        /// </summary>
        /// <param name="nn">The neural network to train</param>
        public GeneticLearningAlgorithm(NeuralNetwork nn)
            : base(nn)
        {
            population = new List<GeneticNeuralNetwork>();
            for (int i = 0; i < POPULATION_SIZE; i++)
                population.Add(Muted_NeuralNetwork);
        }
        /// <summary>
        /// Make a new random population
        /// </summary>
        public void RandomizePopulation()
        {
            for (int i = 0; i < population.Count; i++)
            {
                nn.randomizeAll();
                population[i] = Muted_NeuralNetwork;
            }
        }
        /// <summary>
        /// To train the neuronal network on data.
        /// inputs[n] represents an input vector of 
        /// the neural network and expected_outputs[n]
        /// the expected ouput for this vector. 
        /// </summary>
        /// <param name="inputs">the input matrix</param>
        /// <param name="expected_outputs">the expected output matrix</param>
        public override void Learn(float[][] inputs, float[][] expected_outputs)
        {
            base.Learn(inputs, expected_outputs);

            iter = 0;
            do
            {
                if (iter != 0)
                    makeNewGeneration();
                ComputeErrors();
                population.Sort();
                error = ((GeneticNeuralNetwork)population[0]).Error;
                iter++;
            }
            while (iter < MAX_ITER && this.error > ERROR_THRESHOLD);
            ((GeneticNeuralNetwork)population[0]).setWeights();
        }

        #endregion

        #region GeneticNeuralNetwork
        /// <summary>
        /// Representation of a neural network for the genetic algorithm
        /// </summary>
        //[Serializable]
        protected class GeneticNeuralNetwork : IComparable
        {
            /// <summary>
            /// The genes : all neurons weight and threshold
            /// </summary>
            protected float[] genes;
            /// <summary>
            /// The global square error of the neuron
            /// </summary>
            protected float sq_err = -1f;
            /// <summary>
            /// The neural network of the Genetic Neural Network
            /// </summary>
            protected NeuralNetwork nn;
            /// <summary>
            /// Get or set the genes value
            /// </summary>
            public float this[int index]
            {
                get { return genes[index]; }
                set { genes[index] = value; }
            }
            /// <summary>
            /// Get or set the square error of the Network
            /// </summary>
            public float Error
            {
                get { return sq_err; }
                set { sq_err = value; }
            }
            /// <summary>
            /// Get the number of genes of the Genetic Neural Network
            /// </summary>
            public int N_Genes
            {
                get { return genes.Length; }
            }
            /// <summary>
            /// Build a new Genetic NeuralNetwork from the Neural Network given as parameter
            /// </summary>
            /// <param name="n">The neural network model</param>
            public GeneticNeuralNetwork(NeuralNetwork n)
            {
                nn = n;
                int size = 0;
                for (int i = 0; i < nn.N_Layers; i++)
                    size += (nn[i].N_Inputs + 1) * nn[i].N_Neurons;
                genes = new float[size];
            }
            /// <summary>
            /// Initialize Genetic network from Neural Network
            /// </summary>
            public void Init()
            {
                int index = 0;
                int i, j, k;
                for (i = 0; i < nn.N_Layers; i++)
                    for (j = 0; j < nn[i].N_Neurons; j++)
                    {
                        for (k = 0; k < nn[i][j].N_Inputs; k++)
                            genes[index++] = nn[i][j][k];
                        genes[index++] = nn[i][j].Threshold;
                    }
            }
            /// <summary>
            /// Set Genetic neural network weights to the real neural network
            /// </summary>
            public void setWeights()
            {
                int index = 0;
                int i, j, k;
                for (i = 0; i < nn.N_Layers; i++)
                    for (j = 0; j < nn[i].N_Neurons; j++)
                    {
                        for (k = 0; k < nn[i][j].N_Inputs; k++)
                            nn[i][j][k] = genes[index++];
                        nn[i][j].Threshold = genes[index++];
                    }
            }
            /// <summary>
            /// Compare 2 genetic neural network on their square error
            /// </summary>
            /// <param name="other">another neural network</param>
            /// <returns>the comparative value</returns>
            public int CompareTo(Object other)
            {
                return sq_err.CompareTo(((GeneticNeuralNetwork)other).Error);
            }
        }
        #endregion
    }

    #endregion

    /*
    //Output layer  (possible actions)
    //  varios graus da acção (1/0 ou Fx)
    //n Hidden Layer
    //Input layer   (sensors)

    public class Neuron
    {
        //the number of inputs into the neuron
        int NumInputs;

        //the weights for each input
        IDictionary<int, int> InputWeight;

        public Neuron(int numInputs)
        {
            NumInputs = numInputs;
        }
    }

    public class NeuronLayer
    {
        //the number of neurons in this layer
        int NumNeurons;

        //the layer of neurons
        List<Neuron> Neurons = new List<Neuron>();

        public NeuronLayer(int numNeurons, int numInputsPerNeuron)
        {
            NumNeurons = numNeurons;
            for (int i = 0; i != numNeurons; i++)
            {
                Neurons.Add(new Neuron(numInputsPerNeuron));
            }
        }
    }

    public class NeuralNet
    {
        int NumInputs;
        int NumOutputs;
        int NumHiddenLayers;
        int NeuronsPerHiddenLyr;

        //storage for each layer of neurons including the output layer
        List<NeuronLayer> Layers;

        public NeuralNet() { }

        public void CreateNet() { }

        //gets the weights from the NN
        List<double> GetWeights()
        {
            return null;
        }

        //returns the total number of weights in the net
        public int GetNumberOfWeights()
        {
            return 0;
        }

        //replaces the weights with new ones
        public void PutWeights(List<double> weights) { }

        //calculates the outputs from a set of inputs
        List<double> Update(List<double> inputs)
        {
            return null;
            /*
             * //stores the resultant outputs from each layer

      vector<double> outputs;

 

      int cWeight = 0;

 

      //first check that we have the correct amount of inputs

      if (inputs.size() != m_NumInputs)

      {

        //just return an empty vector if incorrect.

        return outputs;

      }

 


      //For each layer....

      for (int i=0; i<m_NumHiddenLayers + 1; ++i)

      { 

        if ( i > 0 )

        {

          inputs = outputs;

        }

 

        outputs.clear();

 


        cWeight = 0;

 

        //for each neuron sum the (inputs * corresponding weights).Throw 

        //the total at our sigmoid function to get the output.

        for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)

        {

          double netinput = 0;

 

          int NumInputs = m_vecLayers[i].m_vecNeurons[j].m_NumInputs;

 


          //for each weight

          for (int k=0; k<NumInputs - 1; ++k)

          {

            //sum the weights x inputs

            netinput += m_vecLayers[i].m_vecNeurons[j].m_vecWeight[k] * 

                        inputs[cWeight++];

          }

 

          //add in the bias

          netinput += m_vecLayers[i].m_vecNeurons[j].m_vecWeight[NumInputs-1] * 

                      CParams::dBias;

 

          //we can store the outputs from each layer as we generate them. 

          //The combined activation is first filtered through the sigmoid 

          //function

          outputs.push_back(Sigmoid(netinput, CParams::dActivationResponse));

 

          cWeight = 0;

        }

      }

      return outputs;


             
        }

        //sigmoid response curve
        public double Sigmoid(double activation, double response)
        {
            return 0;
        }
    }

    public class Genome
    {
        List<double> Weights;

        double Fitness;

        public Genome()
        {
            Fitness = 0;
        }

        public Genome(List<double> weights, double fitness)
        {
            Weights = weights;
            Fitness = fitness;
        }

        //overload '<' used for sorting
        
          protected bool operator<(const SGenome& lhs, const SGenome& rhs)

          {

            return (lhs.dFitness < rhs.dFitness);

          }
    }

    public class GenAlg
    {
        //this holds the entire population of chromosomes
        List<Genome> population;

        //size of population
        int popSize;

        //amount of weights per chromo
        int chromoLength;

        //total fitness of population
        double totalFitness;

        //best fitness this population
        double bestFitness;

        //average fitness
        double averageFitness;

        //worst
        double worstFitness;

        //keeps track of the best genome
        int FittestGenome;

        //probability that a chromosomes bits will mutate.
        //Try figures around 0.05 to 0.3 ish
        double mutationRate;

        //probability of chromosomes crossing over bits
        //0.7 is pretty good
        double crossoverRate;

        //generation counter
        int generation;

        void Crossover(List<double> mum, List<double> dad, List<double> baby1, List<double> baby2) { }

        void Mutate(List<double> chromo) { }

        Genome GetChromoRoulette()
        {
            return null;
        }

        void GrabNBest(int NBest, int NumCopies, List<Genome> vecPop) { }

        void CalculateBestWorstAvTot() { }

        void Reset() { }

        public GenAlg(int popsize, double MutRat, double CrossRat, int numweights) { }

        //this runs the GA for one generation.
        public List<Genome> Epoch(List<Genome> old_pop)
        {
            return null;
        }

        //-------------------accessor methods
        public List<Genome> GetChromos() { return population; }

        public double AverageFitness() { return totalFitness / popSize; }

        public double BestFitness() { return bestFitness; }
    }
*/
}
